// TestEmbed.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include <string.h> 
#include <mono/jit/jit.h> 
#include <mono/metadata/mono-debug.h>
#include <mono/metadata/object.h>
#include <mono/metadata/reflection.h>
#include <mono/metadata/environment.h>
#include <mono/metadata/assembly.h>
#include <mono/metadata/debug-helpers.h>
#include <mono/metadata/class.h>


MonoDomain *domain = NULL;
MonoAssembly *assembly;
MonoImage *image;
MonoClass * spTypeClrFacade = NULL;

static const char* options[] = {
    "--soft-breakpoints",
    "--debugger-agent=transport=dt_socket,address=127.0.0.1:10000"
};

void mono_create_domain( char* filename, int mono_debug, int from_file, char * runtime_version) {
    if(mono_debug) {
        mono_jit_parse_options(sizeof(options)/sizeof(char*), (char**)options);
        mono_debug_init(MONO_DEBUG_FORMAT_MONO);
    }
    if(from_file)
        domain = mono_jit_init(filename);
    else
        domain = mono_jit_init_version("some_domain", runtime_version);
    
    if (!domain) {
        printf("Failed to create rClr MonoDomain. Requested runtime was %s", runtime_version);
        return;
    }
    else {
        //TODO: have a message that prints the version of the runtime. One seems not to always get the requested one with Mono, somehow. 
        // warning()
        //mono_jit_info_get_code_start
        //mono_jit_info_get_code_start
    }

    assembly = mono_domain_assembly_open (domain, filename);
    if (!assembly) {
        printf("Mono assembly seems to be NULL??");
        return;
    }
    image = mono_assembly_get_image (assembly);
    if (!image) {
        printf("Mono image seems to be NULL??");
        return;
    }
    /*
    * mono_jit_exec() will run the Main() method in the assembly.
    * The return value needs to be looked up from
    * System.Environment.ExitCode.
    */
    mono_jit_exec (domain, assembly, 1, &filename);

    spTypeClrFacade = mono_class_from_name( image, "LibWithExtMethods", "ClassOne");
}


void do_stuff(int from_file, char * filename, char * runtime_version)
{
    mono_create_domain(filename, 0, from_file, runtime_version);
    MonoMethod * method = mono_class_get_method_from_name(spTypeClrFacade, "Execute", 0);
    MonoObject * exception = NULL;
    mono_runtime_invoke(method, NULL, NULL, &exception);
}

#define LIB_PATH "F:\\codeplex\\r2clr\\testing\\LibWithExtMethods\\LibWithExtMethods\\bin\\Debug\\LibWithExtMethods.dll"

void specific_version(char * runtime_version)
{
    int from_file = 0;
    char * filename = LIB_PATH;
    do_stuff(from_file, filename, runtime_version);
}

MonoString * create_mono_string(char * str) {
	const char * str2 = strdup(str);
	return mono_string_new (domain, str2);
}

// Trying a macro by inference from mono's mono_array_set; very shallow understanding of it...
#define create_array_oftype(type, mono_get_some_class, values, length)	\
	do {	\
	int j; \
	MonoArray* monoArray = mono_array_new(domain, mono_get_some_class(), length); \
	for (j = 0; j < length; j++) \
{ \
	mono_array_set(monoArray, type, j, values[j]); \
} \
	return monoArray; \
	} while (0)


MonoArray * create_array_object( void ** values, int length )
{
	create_array_oftype(void*, mono_get_object_class, values, length);
}

void test_staticmethod_call_dynamic(char * runtime_version)
{
    int from_file = 0;
    char * filename = LIB_PATH;
    mono_create_domain(filename, 0, from_file, runtime_version);
    MonoMethod * method = mono_class_get_method_from_name(spTypeClrFacade, "CallStaticMethod", 3);
    MonoObject * exception = NULL;
	void ** meth_mparams = new void* [2];
	void ** static_mparams = new void* [3];
	// MonoArray* methParams = mono_array_new( domain, mono_get_object_class(), 2);
	meth_mparams[0] = create_mono_string("first param");
	int a_bool = 1;
	MonoObject * box_bool = mono_value_box(domain, mono_get_boolean_class(), &a_bool);
	meth_mparams[1] = box_bool; // false
	//meth_mparams[1] = create_mono_string("second param");

	MonoArray* methParams = create_array_object(meth_mparams, 2);

	static_mparams[0] = create_mono_string("some.namespace");
	static_mparams[1] = create_mono_string("some_short_typename");
	static_mparams[2] = methParams;
    mono_runtime_invoke(method, NULL, static_mparams, &exception);
}

void test_method_call_dynamic(char * runtime_version)
{
    int from_file = 0;
    char * filename = LIB_PATH;
    mono_create_domain(filename, 0, from_file, runtime_version);
    MonoMethod * method = mono_class_get_method_from_name(spTypeClrFacade, "CallMethod", 3);
    MonoObject * exception = NULL;
	void ** meth_mparams = new void* [1];
	void ** static_mparams = new void* [3];
	// MonoArray* methParams = mono_array_new( domain, mono_get_object_class(), 2);
	meth_mparams[0] = create_mono_string("postfix");
	MonoObject * the_obj = mono_object_new(domain, mono_class_from_name(image, "LibWithExtMethods", "ClassOne"));

	MonoArray* methParams = create_array_object(meth_mparams, 1);

	static_mparams[0] = the_obj;
	static_mparams[1] = create_mono_string("ConcatValue");
	static_mparams[2] = methParams;
    MonoObject * result = mono_runtime_invoke(method, NULL, static_mparams, &exception);
	printf(mono_class_get_name(mono_object_get_class(result)));
	delete(meth_mparams);
}

void from_exe(char * runtime_version)
{
    int from_file = 1;
    char * filename = "F:\\codeplex\\r2clr\\testing\\LibWithExtMethods\\ConsoleApp\\bin\\Debug\\ConsoleApp.exe";
    do_stuff(from_file, filename, runtime_version);
}

int _tmain(int argc, _TCHAR* argv[])
{
    //from_exe();
    //#define runtime_version "v4.0.30128"
    char * runtime_version = "v4.0.30319";
    //runtime_version = "v4.0.30128";
    test_method_call_dynamic(runtime_version);
}

